//
// (C) Copyright 2009 Irantha Suwandarathna (irantha@gmail.com)
// All rights reserved.
//

/* Copyright (c) 2001-2008, The HSQL Development Group
 * All rights reserved.
 *
 * Redistribution and use _in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * Redistributions of source code must retain the above copyright notice, this
 * list of conditions and the following disclaimer.
 *
 * Redistributions _in binary form must reproduce the above copyright notice,
 * this list of conditions and the following disclaimer _in the documentation
 * and/or other materials provided with the distribution.
 *
 * Neither the name of the HSQL Development Group nor the names of its
 * contributors may be used to endorse or promote products derived from this
 * software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL HSQL DEVELOPMENT GROUP, HSQLDB.ORG,
 * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */


using System;
using System.Collections.Generic;
using System.Text;
using System.Linq;
using EffiProz.Core.Persist;
using EffiProz.Core.Lib;
using EffiProz.Core.Indexes;
using EffiProz.Core.Rights;
using EffiProz.Core.Errors;
using EffiProz.Core.DataTypes;

namespace EffiProz.Core
{

    /**
     * Manages all SCHEMA related database objects
     *
     * @author fredt@users
     * @version  1.8.0
     * @since 1.8.0
     */
    public class SchemaManager
    {

        Database database;
        QNameManager.QName defaultSchemaHsqlName;
        HashMappedList schemaMap = new HashMappedList();
        MultiValueHashMap referenceMap = new MultiValueHashMap();
        int defaultTableType = TableBase.MEMORY_TABLE;
        long schemaChangeTimestamp;

        public SchemaManager(Database database)
        {

            this.database = database;
            defaultSchemaHsqlName = SqlInvariants.INFORMATION_SCHEMA_QNAME;

            Schema schema =
                new Schema(SqlInvariants.INFORMATION_SCHEMA_QNAME,
                           SqlInvariants.INFORMATION_SCHEMA_QNAME.owner);

            schemaMap.put(schema.name.name, schema);

            try
            {
                schema.typeLookup.add(SqlInvariants.CARDINAL_NUMBER);
                schema.typeLookup.add(SqlInvariants.YES_OR_NO);
                schema.typeLookup.add(SqlInvariants.CHARACTER_DATA);
                schema.typeLookup.add(SqlInvariants.SQL_IDENTIFIER);
                schema.typeLookup.add(SqlInvariants.TIME_STAMP);
                schema.charsetLookup.add(SqlInvariants.SQL_TEXT);
                schema.charsetLookup.add(SqlInvariants.SQL_IDENTIFIER_CHARSET);
                schema.charsetLookup.add(SqlInvariants.SQL_CHARACTER);
            }
            catch (CoreException ) { }
        }

        public void setSchemaChangeTimestamp()
        {
            schemaChangeTimestamp = database.txManager.getGlobalChangeTimestamp();
        }

        public long getSchemaChangeTimestamp()
        {
            return schemaChangeTimestamp;
        }

        // pre-defined
        public QNameManager.QName getSQLJSchemaHsqlName()
        {
            return SqlInvariants.SQLJ_SCHEMA_QNAME;
        }

        // SCHEMA management
        public void createPublicSchema()
        {

            QNameManager.QName name = database.nameManager.newHsqlName(null,
                SqlInvariants.PUBLIC_SCHEMA, SchemaObjectTypes.SCHEMA);
            Schema schema = new Schema(name,
                                       database.getGranteeManager().getDBARole());

             defaultSchemaHsqlName = schema.name;

            schemaMap.put(schema.name.name, schema);
        }

        /**
         * Creates a schema belonging to the given grantee.
         */
        public void createSchema(QNameManager.QName name, Grantee owner)
        {

            SqlInvariants.checkSchemaNameNotSystem(name.name);

            Schema schema = new Schema(name, owner);

            schemaMap.add(name.name, schema);
        }

        public void dropSchema(String name, bool cascade)
        {

            Schema schema = (Schema)schemaMap.get(name);

            if (schema == null)
            {
                throw Error.error(ErrorCode.X_42501, name);
            }

            if (cascade)
            {
                OrderedHashSet externalReferences = new OrderedHashSet();

                getCascadingSchemaReferences(schema.getName(), externalReferences);
                removeSchemaObjects(externalReferences);
            }
            else
            {
                if (!schema.isEmpty())
                {
                    throw Error.error(ErrorCode.X_2B000);
                }
            }

            Iterator tableIterator =
                schema.schemaObjectIterator(SchemaObjectTypes.TABLE);

            while (tableIterator.hasNext())
            {
                Table table = ((Table)tableIterator.next());

                database.getGranteeManager().removeDbObject(table.getName());
                table.releaseTriggers();
                database.persistentStoreCollection.releaseStore(table);
            }

            Iterator sequenceIterator =
                schema.schemaObjectIterator(SchemaObjectTypes.SEQUENCE);

            while (sequenceIterator.hasNext())
            {
                NumberSequence sequence =
                    ((NumberSequence)sequenceIterator.next());

                database.getGranteeManager().removeDbObject(sequence.getName());
            }

            schema.clearStructures();
            schemaMap.remove(name);

            if (defaultSchemaHsqlName.name.Equals(name))
            {
                QNameManager.QName hsqlName = database.nameManager.newHsqlName(name, false,
                    SchemaObjectTypes.SCHEMA);

                schema = new Schema(hsqlName,
                                    database.getGranteeManager().getDBARole());
                defaultSchemaHsqlName = schema.name;

                schemaMap.put(schema.name.name, schema);
            }

            // these are called last and in this particular order
            database.getUserManager().removeSchemaReference(name);
            database.getSessionManager().removeSchemaReference(schema);
        }

        public void renameSchema(QNameManager.QName name, QNameManager.QName newName)
        {

            Schema schema = (Schema)schemaMap.get(name.name);
            Schema exists = (Schema)schemaMap.get(newName.name);

            if (schema == null)
            {
                throw Error.error(ErrorCode.X_42501, name.name);
            }

            if (exists != null)
            {
                throw Error.error(ErrorCode.X_42504, newName.name);
            }

            SqlInvariants.checkSchemaNameNotSystem(name.name);
            SqlInvariants.checkSchemaNameNotSystem(newName.name);

            int index = schemaMap.getIndex(name.name);

            schema.name.rename(newName);
            schemaMap.set(index, newName.name, schema);
        }

        public void clearStructures()
        {

            Iterator it = schemaMap.values().iterator();

            while (it.hasNext())
            {
                Schema schema = (Schema)it.next();

                schema.clearStructures();
            }
        }

        public Iterator allSchemaNameIterator()
        {
            return schemaMap.keySet().iterator();
        }

        public QNameManager.QName getUserSchemaHsqlName(String name)
        {

            Schema schema = (Schema)schemaMap.get(name);

            if (schema == null)
            {
                throw Error.error(ErrorCode.X_3F000, name);
            }

            if (schema.getName() == SqlInvariants.INFORMATION_SCHEMA_QNAME)
            {
                throw Error.error(ErrorCode.X_3F000, name);
            }

            return schema.name;
        }

        public Grantee toSchemaOwner(String name)
        {

            // Note that INFORMATION_SCHEMA and DEFINITION_SCHEMA aren't in the
            // backing map.
            // This may not be the most elegant solution, but it is the safest
            // (without doing a code review for implications of adding
            // them to the map).
            if (SqlInvariants.INFORMATION_SCHEMA_QNAME.name.Equals(name))
            {
                return SqlInvariants.INFORMATION_SCHEMA_QNAME.owner;
            }

            Schema schema = (Schema)schemaMap.get(name);

            return schema == null ? null
                                  : schema.owner;
        }

        public QNameManager.QName getDefaultSchemaHsqlName()
        {
            return defaultSchemaHsqlName;
        }

        public void setDefaultSchemaHsqlName(QNameManager.QName name)
        {
            defaultSchemaHsqlName = name;
        }

        public bool schemaExists(String name)
        {
            return SqlInvariants.INFORMATION_SCHEMA.Equals(name)
                   || schemaMap.containsKey(name);
        }

        public QNameManager.QName findSchemaHsqlName(String name)
        {

            Schema schema = ((Schema)schemaMap.get(name));

            if (schema == null)
            {
                return null;
            }

            return schema.name;
        }

        /**
         * If schemaName is null, return the default schema name, else return
         * the QNameManager.QName object for the schema. If schemaName does not exist,
         * throw.
         */
        public QNameManager.QName getSchemaHsqlName(String name)
        {

            if (name == null)
            {
                return defaultSchemaHsqlName;
            }

            if (SqlInvariants.INFORMATION_SCHEMA.Equals(name))
            {
                return SqlInvariants.INFORMATION_SCHEMA_QNAME;
            }

            Schema schema = ((Schema)schemaMap.get(name));

            if (schema == null)
            {
                throw Error.error(ErrorCode.X_3F000, name);
            }

            return schema.name;
        }

        /**
         * Same as above, but return string
         */
        public String getSchemaName(String name)
        {
            return getSchemaHsqlName(name).name;
        }

        /**
         * Iterator includes DEFINITION_SCHEMA
         */
        public Iterator fullSchemaNamesIterator()
        {
            return schemaMap.keySet().iterator();
        }

        public bool isSystemSchema(String schema)
        {

            return SqlInvariants.INFORMATION_SCHEMA.Equals(schema)
                   || SqlInvariants.DEFINITION_SCHEMA.Equals(schema)
                   || SqlInvariants.SYSTEM_SCHEMA.Equals(schema);
        }

        public bool isLobsSchema(String schema)
        {
            return SqlInvariants.LOBS_SCHEMA.Equals(schema);
        }

        /**
         * is a grantee the authorization of any schema
         */
        public bool isSchemaAuthorisation(Grantee grantee)
        {

            Iterator schemas = allSchemaNameIterator();

            while (schemas.hasNext())
            {
                String schemaName = (String)schemas.next();

                if (grantee.Equals(toSchemaOwner(schemaName)))
                {
                    return true;
                }
            }

            return false;
        }

        /**
         * drop all schemas with the given authorisation
         */
        public void dropSchemas(Grantee grantee, bool cascade)
        {

            EfzArrayList list = getSchemas(grantee);
            Iterator it = list.iterator();

            while (it.hasNext())
            {
                Schema schema = (Schema)it.next();

                dropSchema(schema.name.name, cascade);
            }
        }

        public EfzArrayList getSchemas(Grantee grantee)
        {

            EfzArrayList list = new EfzArrayList();
            Iterator it = schemaMap.values().iterator();

            while (it.hasNext())
            {
                Schema schema = (Schema)it.next();

                if (grantee.Equals(schema.owner))
                {
                    list.add(schema);
                }
            }

            return list;
        }

        public bool hasSchemas(Grantee grantee)
        {

            Iterator it = schemaMap.values().iterator();

            while (it.hasNext())
            {
                Schema schema = (Schema)it.next();

                if (grantee.Equals(schema.owner))
                {
                    return true;
                }
            }

            return false;
        }

        /**
         *  Returns an HsqlArrayList containing references to all non-system
         *  tables and views. This includes all tables and views registered with
         *  this Database.
         */
        public EfzArrayList getAllTables()
        {

            Iterator schemas = allSchemaNameIterator();
            EfzArrayList alltables = new EfzArrayList();

            while (schemas.hasNext())
            {
                String name = (String)schemas.next();
                HashMappedList current = getTables(name);

                alltables.addAll(current.values());
            }

            return alltables;
        }

        public HashMappedList getTables(String schema)
        {

            Schema temp = (Schema)schemaMap.get(schema);

            return temp.tableList;
        }

        public QNameManager.QName[] getCatalogAndBaseTableNames()
        {

            OrderedHashSet names = new OrderedHashSet();
            EfzArrayList tables = getAllTables();

            for (int i = 0; i < tables.size(); i++)
            {
                Table table = (Table)tables.get(i);

                if (!table.isTemp())
                {
                    names.add(table.getName());
                }
            }

            names.add(database.getCatalogName());

            QNameManager.QName[] array = new QNameManager.QName[names.size()];

            names.toArray(array);

            return array;
        }

        SchemaObjectSet getSchemaObjectSet(Schema schema, int type)
        {

            SchemaObjectSet set = null;

            switch (type)
            {

                case SchemaObjectTypes.SEQUENCE:
                    set = schema.sequenceLookup;
                    break;

                case SchemaObjectTypes.TABLE:
                case SchemaObjectTypes.VIEW:
                    set = schema.tableLookup;
                    break;

                case SchemaObjectTypes.CHARSET:
                    set = schema.charsetLookup;
                    break;

                case SchemaObjectTypes.COLLATION:
                    set = schema.collationLookup;
                    break;

                case SchemaObjectTypes.PROCEDURE:
                    set = schema.procedureLookup;
                    break;

                case SchemaObjectTypes.FUNCTION:
                    set = schema.functionLookup;
                    break;

                case SchemaObjectTypes.DOMAIN:
                case SchemaObjectTypes.TYPE:
                    set = schema.typeLookup;
                    break;

                case SchemaObjectTypes.INDEX:
                    set = schema.indexLookup;
                    break;

                case SchemaObjectTypes.CONSTRAINT:
                    set = schema.constraintLookup;
                    break;

                case SchemaObjectTypes.TRIGGER:
                    set = schema.triggerLookup;
                    break;

                case SchemaObjectTypes.SPECIFIC_ROUTINE:
                    set = schema.specificRoutineLookup;
                    break;
            }

            return set;
        }

        public void checkSchemaObjectNotExists(QNameManager.QName name)
        {

            Schema schema = (Schema)schemaMap.get(name.schema.name);
            SchemaObjectSet set = getSchemaObjectSet(schema, name.type);

            set.checkAdd(name);
        }

        /**
         *  Returns the specified user-defined table or view visible within the
         *  context of the specified Session, or any system table of the given
         *  name. It excludes any temp tables created in other Sessions.
         *  Throws if the table does not exist in the context.
         */
        public Table getTable(Session session, String name, String schema)
        {

            Table t = null;

            if (schema == null)
            {
                t = findSessionTable(session, name, null);
            }

            if (t == null)
            {
                schema = session.getSchemaName(schema);
                t = findUserTable(session, name, schema);
            }

            if (t == null)
            {
                if (SqlInvariants.INFORMATION_SCHEMA.Equals(schema)
                        && database.dbInfo != null)
                {
                    t = database.dbInfo.getSystemTable(session, name);
                }
            }

            if (t == null)
            {
                throw Error.error(ErrorCode.X_42501, name);
            }

            return t;
        }

        public Table getUserTable(Session session, QNameManager.QName name)
        {
            return getUserTable(session, name.name, name.schema.name);
        }

        /**
         *  Returns the specified user-defined table or view visible within the
         *  context of the specified Session. It excludes system tables and
         *  any temp tables created in different Sessions.
         *  Throws if the table does not exist in the context.
         */
        public Table getUserTable(Session session, String name, String schema)
        {

            Table t = findUserTable(session, name, schema);

            if (t == null)
            {
                throw Error.error(ErrorCode.X_42501, name);
            }

            return t;
        }

        /**
         *  Returns the specified user-defined table or view visible within the
         *  context of the specified schema. It excludes system tables.
         *  Returns null if the table does not exist in the context.
         */
        public Table findUserTable(Session session, String name,
                                   String schemaName)
        {

            Schema schema = (Schema)schemaMap.get(schemaName);

            if (schema == null)
            {
                return null;
            }

            int i = schema.tableList.getIndex(name);

            if (i == -1)
            {
                return null;
            }

            return (Table)schema.tableList.get(i);
        }

        /**
         *  Returns the specified session context table.
         *  Returns null if the table does not exist in the context.
         */
        public Table findSessionTable(Session session, String name,
                                      String schemaName)
        {
            return session.findSessionTable(name);
        }

        /**
         * Drops the specified user-defined view or table from this Database object.
         *
         * <p> The process of dropping a table or view includes:
         * <OL>
         * <LI> checking that the specified Session's currently connected User has
         * the right to perform this operation and refusing to proceed if not by
         * throwing.
         * <LI> checking for referential constraints that conflict with this
         * operation and refusing to proceed if they exist by throwing.</LI>
         * <LI> removing the specified Table from this Database object.
         * <LI> removing any exported foreign keys Constraint objects held by any
         * tables referenced by the table to be dropped. This is especially
         * important so that the dropped Table ceases to be referenced, eventually
         * allowing its full garbage collection.
         * <LI>
         * </OL>
         *
         * <p>
         *
         * @param session the connected context in which to perform this operation
         * @param table if true and if the Table to drop does not exist, fail
         *   silently, else throw
         * @param cascade true if the name argument refers to a View
         */
        public void dropTableOrView(Session session, Table table, bool cascade)
        {

            // ft - concurrent
            session.commit(false);

            if (table.isView())
            {
                removeSchemaObject(table.getName(), cascade);
            }
            else
            {
                dropTable(session, table, cascade);
            }
        }

        public void dropTable(Session session, Table table, bool cascade)
        {

            Schema schema = (Schema)schemaMap.get(table.getSchemaName().name);
            int dropIndex = schema.tableList.getIndex(table.getName().name);
            OrderedHashSet externalConstraints =
                table.getDependentExternalConstraints();
            OrderedHashSet externalReferences = new OrderedHashSet();

            getCascadingReferences(table.getName(), externalReferences);

            if (!cascade)
            {
                for (int i = 0; i < externalConstraints.size(); i++)
                {
                    Constraint c = (Constraint)externalConstraints.get(i);
                    QNameManager.QName tablename = c.getRef().getName();
                    QNameManager.QName refname = c.getRefName();

                    if (c.getConstraintType()
                            == SchemaObjectConstraintTypes.MAIN)
                    {
                        throw Error.error(ErrorCode.X_42533,
                                          refname.schema.name + '.'
                                          + tablename.name + '.' + refname.name);
                    }
                }

                if (!externalReferences.isEmpty())
                {
                    int i = 0;

                    for (; i < externalReferences.size(); i++)
                    {
                        QNameManager.QName name = (QNameManager.QName)externalReferences.get(i);

                        if (name.parent == table.getName())
                        {
                            continue;
                        }

                        throw Error.error(ErrorCode.X_42502,
                                          name.getSchemaQualifiedStatementName());
                    }
                }
            }

            OrderedHashSet tableSet = new OrderedHashSet();
            OrderedHashSet constraintNameSet = new OrderedHashSet();
            OrderedHashSet indexNameSet = new OrderedHashSet();

            for (int i = 0; i < externalConstraints.size(); i++)
            {
                Constraint c = (Constraint)externalConstraints.get(i);
                Table t = c.getMain();

                if (t != table)
                {
                    tableSet.add(t);
                }

                t = c.getRef();

                if (t != table)
                {
                    tableSet.add(t);
                }

                constraintNameSet.add(c.getMainName());
                constraintNameSet.add(c.getRefName());
                indexNameSet.add(c.getRefIndex().getName());
            }

            TableWorks tw = new TableWorks(session, table);

            tableSet = tw.makeNewTables(tableSet, constraintNameSet, indexNameSet);

            tw.setNewTablesInSchema(tableSet);
            tw.updateConstraints(tableSet, constraintNameSet);
            removeSchemaObjects(externalReferences);
            removeReferencedObject(table.getName());
            schema.tableList.remove(dropIndex);
            database.getGranteeManager().removeDbObject(table.getName());
            schema.triggerLookup.removeParent(table.tableName);
            schema.indexLookup.removeParent(table.tableName);
            schema.constraintLookup.removeParent(table.tableName);
            table.releaseTriggers();
            database.persistentStoreCollection.releaseStore(table);
            recompileDependentObjects(tableSet);
        }

        public void setTable(int index, Table table)
        {

            Schema schema = (Schema)schemaMap.get(table.getSchemaName().name);

            schema.tableList.set(index, table.getName().name, table);
        }

        /**
         *  Returns index of a table or view in the HashMappedList that
         *  contains the table objects for this Database.
         *
         * @param  table the Table object
         * @return  the index of the specified table or view, or -1 if not found
         */
        public int getTableIndex(Table table)
        {

            Schema schema = (Schema)schemaMap.get(table.getSchemaName().name);

            if (schema == null)
            {
                return -1;
            }

            QNameManager.QName name = table.getName();

            return schema.tableList.getIndex(name.name);
        }

        public void recompileDependentObjects(OrderedHashSet tableSet)
        {

            OrderedHashSet set = new OrderedHashSet();

            for (int i = 0; i < tableSet.size(); i++)
            {
                Table table = (Table)tableSet.get(i);

                set.addAll(getReferencingObjects(table.getName()));
            }

            Session session = database.sessionManager.getSysSession();

            for (int i = 0; i < set.size(); i++)
            {
                QNameManager.QName name = (QNameManager.QName)set.get(i);

                switch (name.type)
                {

                    case SchemaObjectTypes.VIEW:
                    case SchemaObjectTypes.CONSTRAINT:
                    case SchemaObjectTypes.ASSERTION:
                        SchemaObject obj = getSchemaObject(name);

                        obj.compile(session, null);
                        break;
                }
            }
        }

        /**
         * After addition or removal of columns and indexes all views that
         * reference the table should be recompiled.
         */
        public void recompileDependentObjects(Table table)
        {

            OrderedHashSet set = getReferencingObjects(table.getName());
            Session session = database.sessionManager.getSysSession();

            for (int i = 0; i < set.size(); i++)
            {
                QNameManager.QName name = (QNameManager.QName)set.get(i);

                switch (name.type)
                {

                    case SchemaObjectTypes.VIEW:
                    case SchemaObjectTypes.CONSTRAINT:
                    case SchemaObjectTypes.ASSERTION:
                        SchemaObject obj = getSchemaObject(name);

                        obj.compile(session, null);
                        break;
                }
            }

            EfzArrayList list = getAllTables();

            for (int i = 0; i < list.size(); i++)
            {
                Table t = (Table)list.get(i);

                t.updateConstraintPath();
            }
        }

        public NumberSequence getSequence(String name, String schemaName, bool raise)
        {

            Schema schema = (Schema)schemaMap.get(schemaName);

            if (schema != null)
            {
                NumberSequence obj =
                    (NumberSequence)schema.sequenceList.get(name);

                if (obj != null)
                {
                    return obj;
                }
            }

            if (raise)
            {
                throw Error.error(ErrorCode.X_42501, name);
            }

            return null;
        }

        public SqlType getUserDefinedType(String name, String schemaName,
                                       bool raise)
        {

            Schema schema = (Schema)schemaMap.get(schemaName);

            if (schema != null)
            {
                SchemaObject obj = schema.typeLookup.getObject(name);

                if (obj != null)
                {
                    return (SqlType)obj;
                }
            }

            if (raise)
            {
                throw Error.error(ErrorCode.X_42501, name);
            }

            return null;
        }

        public SqlType getDomain(String name, String schemaName, bool raise)
        {

            Schema schema = (Schema)schemaMap.get(schemaName);

            if (schema != null)
            {
                SchemaObject obj = schema.typeLookup.getObject(name);

                if (obj != null && ((SqlType)obj).isDomainType())
                {
                    return (SqlType)obj;
                }
            }

            if (raise)
            {
                throw Error.error(ErrorCode.X_42501, name);
            }

            return null;
        }

        public SqlType getDistinctType(String name, String schemaName,
                                    bool raise) {

        Schema schema = (Schema) schemaMap.get(schemaName);

        if (schema != null) {
            SchemaObject obj = schema.typeLookup.getObject(name);

            if (obj != null && ((SqlType) obj).isDomainType()) {
                return (SqlType) obj;
            }
        }

        if (raise) {
            throw Error.error(ErrorCode.X_42501, name);
        }

        return null;
    }

        public SchemaObject getSchemaObject(String name, String schemaName,
                                            int type)
        {

            SchemaObject obj = findSchemaObject(name, schemaName, type);

            if (obj == null)
            {
                throw Error.error(SchemaObjectSet.getGetErrorCode(type), name);
            }

            return obj;
        }

        public SchemaObject findSchemaObject(String name, String schemaName,
                                             int type)
        {

            Schema schema = (Schema)schemaMap.get(schemaName);

            if (schema == null)
            {
                return null;
            }

            SchemaObjectSet set = null;
            QNameManager.QName objectName;
            Table table;

            switch (type)
            {

                case SchemaObjectTypes.SEQUENCE:
                    return schema.sequenceLookup.getObject(name);

                case SchemaObjectTypes.TABLE:
                case SchemaObjectTypes.VIEW:
                    return schema.sequenceLookup.getObject(name);

                case SchemaObjectTypes.CHARSET:
                    if (name.Equals("SQL_IDENTIFIER"))
                    {
                        return SqlInvariants.SQL_IDENTIFIER_CHARSET;
                    }

                    if (name.Equals("SQL_TEXT"))
                    {
                        return SqlInvariants.SQL_TEXT;
                    }

                    if (name.Equals("LATIN1"))
                    {
                        return SqlInvariants.LATIN1;
                    }

                    if (name.Equals("ASCII_GRAPHIC"))
                    {
                        return SqlInvariants.ASCII_GRAPHIC;
                    }

                    return schema.charsetLookup.getObject(name);

                case SchemaObjectTypes.COLLATION:
                    return schema.collationLookup.getObject(name);

                case SchemaObjectTypes.PROCEDURE:
                    return schema.procedureLookup.getObject(name);

                case SchemaObjectTypes.FUNCTION:
                    return schema.functionLookup.getObject(name);

                case SchemaObjectTypes.ROUTINE:
                    {
                        SchemaObject obj = schema.procedureLookup.getObject(name);

                        if (obj == null)
                        {
                            obj = schema.functionLookup.getObject(name);
                        }

                        return obj;
                    }
                case SchemaObjectTypes.SPECIFIC_ROUTINE:
                    return schema.specificRoutineLookup.getObject(name);

                case SchemaObjectTypes.DOMAIN:
                case SchemaObjectTypes.TYPE:
                    return schema.typeLookup.getObject(name);

                case SchemaObjectTypes.INDEX:
                    set = schema.indexLookup;
                    objectName = set.getName(name);

                    if (objectName == null)
                    {
                        return null;
                    }

                    table = (Table)schema.tableList.get(objectName.parent.name);

                    return table.getIndex(name);

                case SchemaObjectTypes.CONSTRAINT:
                    set = schema.constraintLookup;
                    objectName = set.getName(name);

                    if (objectName == null)
                    {
                        return null;
                    }

                    table = (Table)schema.tableList.get(objectName.parent.name);

                    if (table == null)
                    {
                        return null;
                    }

                    return table.getConstraint(name);

                case SchemaObjectTypes.TRIGGER:
                    set = schema.indexLookup;
                    objectName = set.getName(name);

                    if (objectName == null)
                    {
                        return null;
                    }

                    table = (Table)schema.tableList.get(objectName.parent.name);

                    return table.getTrigger(name);

                default:
                    throw Error.runtimeError(ErrorCode.U_S0500, "SchemaManager");
            }
        }

        // INDEX management

        /**
         * Returns the table that has an index with the given name and schema.
         */
        public Table findUserTableForIndex(Session session, String name,
                                     String schemaName)
        {

            Schema schema = (Schema)schemaMap.get(schemaName);
            QNameManager.QName indexName = schema.indexLookup.getName(name);

            if (indexName == null)
            {
                return null;
            }

            return findUserTable(session, indexName.parent.name, schemaName);
        }

        /**
         * Drops the index with the specified name.
         */
        public void dropIndex(Session session, QNameManager.QName name)
        {

            Table t = getTable(session, name.parent.name, name.parent.schema.name);
            TableWorks tw = new TableWorks(session, t);

            tw.dropIndex(name.name);
        }

        /**
         * Drops the index with the specified name.
         */
        public void dropConstraint(Session session, QNameManager.QName name, bool cascade)
        {

            Table t = getTable(session, name.parent.name, name.parent.schema.name);
            TableWorks tw = new TableWorks(session, t);

            tw.dropConstraint(name.name, cascade);
        }

        public void removeDependentObjects(QNameManager.QName name)
        {

            Schema schema = (Schema)schemaMap.get(name.schema.name);

            schema.indexLookup.removeParent(name);
            schema.constraintLookup.removeParent(name);
            schema.triggerLookup.removeParent(name);
        }

        /**
         *  Removes any foreign key Constraint objects (exported keys) held by any
         *  tables referenced by the specified table. <p>
         *
         *  This method is called as the last step of a successful call to
         *  dropTable() in order to ensure that the dropped Table ceases to be
         *  referenced when enforcing referential integrity.
         *
         * @param  toDrop The table to which other tables may be holding keys.
         *      This is a table that is in the process of being dropped.
         */
        public void removeExportedKeys(Table toDrop)
        {

            // toDrop.schema may be null because it is not registerd
            Schema schema = (Schema)schemaMap.get(toDrop.getSchemaName().name);

            for (int i = 0; i < schema.tableList.size(); i++)
            {
                Table table = (Table)schema.tableList.get(i);

                for (int j = table.constraintList.Length - 1; j >= 0; j--)
                {
                    Table refTable = table.constraintList[j].getRef();

                    if (toDrop == refTable)
                    {
                        table.removeConstraint(j);
                    }
                }
            }
        }

        public Iterator databaseObjectIterator(String schemaName, int type)
        {

            Schema schema = (Schema)schemaMap.get(schemaName);

            return schema.schemaObjectIterator(type);
        }

        public Iterator databaseObjectIterator(int type)
        {

            Iterator it = schemaMap.values().iterator();
            Iterator objects = new WrapperIterator();

            while (it.hasNext())
            {
                Schema temp = (Schema)it.next();

                objects = new WrapperIterator(objects,
                                              temp.schemaObjectIterator(type));
            }

            return objects;
        }

        // references
        private void addReferences(SchemaObject obj)
        {

            OrderedHashSet set = obj.getReferences();

            if (set == null)
            {
                return;
            }

            for (int i = 0; i < set.size(); i++)
            {
                QNameManager.QName referenced = (QNameManager.QName)set.get(i);

                if (referenced.type == SchemaObjectTypes.COLUMN)
                {
                    referenceMap.put(referenced.parent, obj.getName());
                }
                else
                {
                    referenceMap.put(referenced, obj.getName());
                }
            }
        }

        private void removeReferencedObject(QNameManager.QName referenced)
        {
            referenceMap.remove(referenced);
        }

        private void removeReferencingObject(SchemaObject obj)
        {

            OrderedHashSet set = obj.getReferences();

            if (set == null)
            {
                return;
            }

            for (int i = 0; i < set.size(); i++)
            {
                QNameManager.QName referenced = (QNameManager.QName)set.get(i);

                referenceMap.remove(referenced, obj.getName());
            }
        }

       public OrderedHashSet getReferencingObjects(QNameManager.QName obj)
        {

            OrderedHashSet set = new OrderedHashSet();
            Iterator it = referenceMap.get(obj);

            while (it.hasNext())
            {
                QNameManager.QName name = (QNameManager.QName)it.next();

                set.add(name);
            }

            return set;
        }

        public OrderedHashSet getReferencingObjects(QNameManager.QName table, QNameManager.QName column)
        {

            OrderedHashSet set = new OrderedHashSet();
            Iterator it = referenceMap.get(table);

            while (it.hasNext())
            {
                QNameManager.QName name = (QNameManager.QName)it.next();
                SchemaObject obj = getSchemaObject(name);
                OrderedHashSet references = obj.getReferences();

                if (references.contains(column))
                {
                    set.add(name);
                }
            }

            return set;
        }

        private bool isReferenced(QNameManager.QName obj)
        {
            return referenceMap.containsKey(obj);
        }

        //
        public void getCascadingReferences(QNameManager.QName obj, OrderedHashSet set)
        {

            OrderedHashSet newSet = new OrderedHashSet();
            Iterator it = referenceMap.get(obj);

            while (it.hasNext())
            {
                QNameManager.QName name = (QNameManager.QName)it.next();
                bool added = set.add(name);

                if (added)
                {
                    newSet.add(name);
                }
            }

            for (int i = 0; i < newSet.size(); i++)
            {
                QNameManager.QName name = (QNameManager.QName)newSet.get(i);

                getCascadingReferences(name, set);
            }
        }

        //
        public void getCascadingSchemaReferences(QNameManager.QName schema, OrderedHashSet set)
        {

            Iterator mainIterator = referenceMap.keySet().iterator();

            while (mainIterator.hasNext())
            {
                QNameManager.QName name = (QNameManager.QName)mainIterator.next();

                if (name.schema != schema)
                {
                    continue;
                }

                getCascadingReferences(name, set);
            }

            for (int i = 0; i < set.size(); i++)
            {
                QNameManager.QName name = (QNameManager.QName)set.get(i);

                if (name.schema == schema)
                {
                    set.remove(i);

                    i--;
                }
            }
        }

        //
        public QNameManager.QName getSchemaObjectName(QNameManager.QName schemaName, String name, int type,
                                      bool raise)
        {

            Schema schema = (Schema)schemaMap.get(schemaName.name);
            SchemaObjectSet set = null;

            if (schema == null)
            {
                if (raise)
                {
                    throw Error.error(SchemaObjectSet.getGetErrorCode(type));
                }
                else
                {
                    return null;
                }
            }

            set = getSchemaObjectSet(schema, type);

            if (raise)
            {
                set.checkExists(name);
            }

            return set.getName(name);
        }

        public SchemaObject getSchemaObject(QNameManager.QName name)
        {

            Schema schema = (Schema)schemaMap.get(name.schema.name);

            if (schema == null)
            {
                return null;
            }

            switch (name.type)
            {

                case SchemaObjectTypes.SEQUENCE:
                    return (SchemaObject)schema.sequenceList.get(name.name);

                case SchemaObjectTypes.TABLE:
                case SchemaObjectTypes.VIEW:
                    return (SchemaObject)schema.tableList.get(name.name);

                case SchemaObjectTypes.CHARSET:
                    return schema.charsetLookup.getObject(name.name);

                case SchemaObjectTypes.COLLATION:
                    return schema.collationLookup.getObject(name.name);

                case SchemaObjectTypes.PROCEDURE:
                    return schema.procedureLookup.getObject(name.name);

                case SchemaObjectTypes.FUNCTION:
                    return schema.functionLookup.getObject(name.name);

                case SchemaObjectTypes.SPECIFIC_ROUTINE:
                    return schema.specificRoutineLookup.getObject(name.name);

                case SchemaObjectTypes.DOMAIN:
                case SchemaObjectTypes.TYPE:
                    return schema.typeLookup.getObject(name.name);

                case SchemaObjectTypes.TRIGGER:
                    {
                        name = schema.triggerLookup.getName(name.name);

                        if (name == null)
                        {
                            return null;
                        }

                        QNameManager.QName tableName = name.parent;
                        Table table = (Table)schema.tableList.get(tableName.name);

                        return table.getTrigger(name.name);
                    }
                case SchemaObjectTypes.CONSTRAINT:
                    {
                        name = schema.constraintLookup.getName(name.name);

                        if (name == null)
                        {
                            return null;
                        }

                        QNameManager.QName tableName = name.parent;
                        Table table = (Table)schema.tableList.get(tableName.name);

                        return table.getConstraint(name.name);
                    }
                case SchemaObjectTypes.ASSERTION:
                    return null;

                case SchemaObjectTypes.INDEX:
                    name = schema.indexLookup.getName(name.name);

                    if (name == null)
                    {
                        return null;
                    }

                    QNameManager.QName tableName1 = name.parent;
                    Table table1 = (Table)schema.tableList.get(tableName1.name);

                    return table1.getIndex(name.name);
            }

            return null;
        }

        public void checkColumnIsReferenced(QNameManager.QName tableName, QNameManager.QName name)
        {

            OrderedHashSet set = getReferencingObjects(tableName, name);

            if (!set.isEmpty())
            {
                QNameManager.QName objectName = (QNameManager.QName)set.get(0);

                throw Error.error(ErrorCode.X_42502,
                                  objectName.getSchemaQualifiedStatementName());
            }
        }

        public void checkObjectIsReferenced(QNameManager.QName name)
        {

            OrderedHashSet set = getReferencingObjects(name);
            QNameManager.QName refName = null;

            for (int i = 0; i < set.size(); i++)
            {
                refName = (QNameManager.QName)set.get(i);

                // except columns of same table
                if (refName.parent != name)
                {
                    break;
                }

                refName = null;
            }

            if (refName == null)
            {
                return;
            }

            throw Error.error(ErrorCode.X_42502,
                              refName.getSchemaQualifiedStatementName());
        }

        public void checkSchemaNameCanChange(QNameManager.QName name)
        {

            Iterator it = referenceMap.values().iterator();
            QNameManager.QName refName = null;


            while (it.hasNext())
            {
                refName = (QNameManager.QName)it.next();

                switch (refName.type)
                {

                    case SchemaObjectTypes.VIEW:
                    case SchemaObjectTypes.ROUTINE:
                    case SchemaObjectTypes.FUNCTION:
                    case SchemaObjectTypes.PROCEDURE:
                        if (refName.schema == name)
                        {
                            goto mainLoop;
                        }
                        break;

                    default:
                        break;
                }

                refName = null;
            }
        mainLoop:
            if (refName == null)
            {
                return;
            }

            throw Error.error(ErrorCode.X_42502,
                              refName.getSchemaQualifiedStatementName());
        }

        public void addSchemaObject(SchemaObject obj)
        {

            QNameManager.QName name = obj.getName();
            Schema schema = (Schema)schemaMap.get(name.schema.name);
            SchemaObjectSet set = getSchemaObjectSet(schema, name.type);

            switch (name.type)
            {

                case SchemaObjectTypes.PROCEDURE:
                case SchemaObjectTypes.FUNCTION:
                    RoutineSchema routine =
                        (RoutineSchema)set.getObject(name.name);

                    if (routine == null)
                    {
                        routine = new RoutineSchema(name.type, name);

                        routine.addSpecificRoutine(database, (Routine)obj);
                        set.checkAdd(name);

                        SchemaObjectSet specificSet = getSchemaObjectSet(schema,
                            SchemaObjectTypes.SPECIFIC_ROUTINE);

                        specificSet.checkAdd(((Routine)obj).getSpecificName());
                        set.add(routine);
                        specificSet.add(obj);
                    }
                    else
                    {
                        SchemaObjectSet specificSet = getSchemaObjectSet(schema,
                            SchemaObjectTypes.SPECIFIC_ROUTINE);
                        QNameManager.QName specificName =
                            ((Routine)obj).getSpecificName();

                        if (specificName != null)
                        {
                            specificSet.checkAdd(specificName);
                        }

                        routine.addSpecificRoutine(database, (Routine)obj);
                        specificSet.add(obj);
                    }

                    addReferences(obj);

                    return;
            }

            set.add(obj);
            addReferences(obj);
        }

        public void removeSchemaObject(QNameManager.QName name, bool cascade)
        {

            OrderedHashSet objectSet = new OrderedHashSet();

            switch (name.type)
            {

                case SchemaObjectTypes.PROCEDURE:
                case SchemaObjectTypes.FUNCTION:
                    {
                        RoutineSchema routine = (RoutineSchema)getSchemaObject(name);

                        if (routine != null)
                        {
                            Routine[] specifics = routine.getSpecificRoutines();

                            for (int i = 0; i < specifics.Length; i++)
                            {
                                getCascadingReferences(specifics[i].getSpecificName(),
                                                       objectSet);
                            }
                        }
                    }
                    goto case SchemaObjectTypes.SPECIFIC_ROUTINE;
                // fall through
                case SchemaObjectTypes.SEQUENCE:
                case SchemaObjectTypes.TABLE:
                case SchemaObjectTypes.VIEW:
                case SchemaObjectTypes.TYPE:
                case SchemaObjectTypes.CHARSET:
                case SchemaObjectTypes.COLLATION:
                case SchemaObjectTypes.SPECIFIC_ROUTINE:
                    getCascadingReferences(name, objectSet);
                    break;

                case SchemaObjectTypes.DOMAIN:
                    break;
            }

            if (objectSet.isEmpty())
            {
                removeSchemaObject(name);

                return;
            }

            if (!cascade)
            {
                QNameManager.QName objectName = (QNameManager.QName)objectSet.get(0);

                throw Error.error(ErrorCode.X_42502,
                                  objectName.getSchemaQualifiedStatementName());
            }

            objectSet.add(name);
            removeSchemaObjects(objectSet);
        }

        public void removeSchemaObjects(OrderedHashSet set)
        {

            for (int i = 0; i < set.size(); i++)
            {
                QNameManager.QName name = (QNameManager.QName)set.get(i);

                removeSchemaObject(name);
            }
        }

        public void removeSchemaObject(QNameManager.QName name) {

        Schema          schema = (Schema) schemaMap.get(name.schema.name);
        SchemaObject    obj = null;
        SchemaObjectSet set    = null;

        switch (name.type) {

            case SchemaObjectTypes.SEQUENCE :
                set    = schema.sequenceLookup;
                obj = set.getObject(name.name);
                break;

            case SchemaObjectTypes.TABLE :
            case SchemaObjectTypes.VIEW : {
                set    = schema.tableLookup;
                obj = set.getObject(name.name);

                set.remove(name.name);

                break;
            }
            case SchemaObjectTypes.CHARSET :
                set    = schema.charsetLookup;
                obj = set.getObject(name.name);
                break;

            case SchemaObjectTypes.COLLATION :
                set    = schema.collationLookup;
                obj = set.getObject(name.name);
                break;

            case SchemaObjectTypes.PROCEDURE : {
                set = schema.procedureLookup;

                RoutineSchema routine =
                    (RoutineSchema) set.getObject(name.name);

                obj = routine;

                Routine[] specifics = routine.getSpecificRoutines();

                for (int i = 0; i < specifics.Length; i++) {
                    removeSchemaObject(specifics[i].getSpecificName());
                }

                break;
            }
            case SchemaObjectTypes.FUNCTION : {
                set = schema.functionLookup;

                RoutineSchema routine =
                    (RoutineSchema) set.getObject(name.name);

                obj = routine;

                Routine[] specifics = routine.getSpecificRoutines();

                for (int i = 0; i < specifics.Length; i++) {
                    removeSchemaObject(specifics[i].getSpecificName());
                }

                break;
            }
            case SchemaObjectTypes.SPECIFIC_ROUTINE : {
                set = schema.specificRoutineLookup;

                Routine routine = (Routine) set.getObject(name.name);

                obj = routine;

                routine.routineSchema.removeSpecificRoutine(routine);

                if (routine.routineSchema.getSpecificRoutines().Length == 0) {
                    removeSchemaObject(routine.getName());
                }

                break;
            }
            case SchemaObjectTypes.DOMAIN :
            case SchemaObjectTypes.TYPE :
                set    = schema.typeLookup;
                obj = set.getObject(name.name);
                break;

            case SchemaObjectTypes.INDEX :
                set = schema.indexLookup;
                break;

            case SchemaObjectTypes.CONSTRAINT : {
                set = schema.constraintLookup;

                if (name.parent.type == SchemaObjectTypes.TABLE) {
                    Table table =
                        (Table) schema.tableList.get(name.parent.name);

                    obj = table.getConstraint(name.name);

                    table.removeConstraint(name.name);
                } else if (name.parent.type == SchemaObjectTypes.DOMAIN) {
                    SqlType type =
                        (SqlType) schema.typeLookup.getObject(name.parent.name);

                    obj = type.userTypeModifier.getConstraint(name.name);

                    type.userTypeModifier.removeConstraint(name.name);
                }

                break;
            }
            case SchemaObjectTypes.TRIGGER : {
                set = schema.triggerLookup;

                Table table = (Table) schema.tableList.get(name.parent.name);

                obj = table.getTrigger(name.name);

                table.removeTrigger(name.name);

                break;
            }
            default :
                throw Error.runtimeError(ErrorCode.U_S0500, "SchemaManager");
        }

        if (obj != null) {
            database.getGranteeManager().removeDbObject(name);
            removeReferencingObject(obj);
        }

        set.remove(name.name);
        removeReferencedObject(name);
    }

        public void renameSchemaObject(QNameManager.QName name, QNameManager.QName newName)
        {

            if (name.schema != newName.schema)
            {
                throw Error.error(ErrorCode.X_42505, newName.schema.name);
            }

            checkObjectIsReferenced(name);

            Schema schema = (Schema)schemaMap.get(name.schema.name);
            SchemaObjectSet set = getSchemaObjectSet(schema, name.type);

            set.rename(name, newName);
        }

        public String[] getSQLArray()
        {

            OrderedHashSet resolved = new OrderedHashSet();
            OrderedHashSet unresolved = new OrderedHashSet();
            EfzArrayList list = new EfzArrayList();
            Iterator schemas = schemaMap.values().iterator();

            while (schemas.hasNext())
            {
                Schema schema = (Schema)schemas.next();

                if (isSystemSchema(schema.name.name))
                {
                    continue;
                }

                if (isLobsSchema(schema.name.name))
                {
                    continue;
                }

                list.addAll(schema.getSQLArray(resolved, unresolved));
            }

            while (true)
            {
                Iterator it = unresolved.iterator();

                if (!it.hasNext())
                {
                    break;
                }

                while (it.hasNext())
                {
                    SchemaObject obj = (SchemaObject)it.next();
                    OrderedHashSet references = obj.getReferences();
                    bool isResolved = true;

                    for (int j = 0; j < references.size(); j++)
                    {
                        QNameManager.QName name = (QNameManager.QName)references.get(j);

                        if (name.type == SchemaObjectTypes.COLUMN
                                || name.type == SchemaObjectTypes.CONSTRAINT)
                        {
                            name = name.parent;
                        }

                        if (!resolved.contains(name))
                        {
                            isResolved = false;

                            break;
                        }
                    }

                    if (isResolved)
                    {
                        if (obj.getType() == SchemaObjectTypes.TABLE)
                        {
                            list.addAll(((Table)obj).getSQL(resolved,
                                                                unresolved));
                        }
                        else
                        {
                            list.add(obj.getSQL());
                            resolved.add(obj.getName());
                        }

                        it.remove();
                    }
                }
            }

            schemas = schemaMap.values().iterator();

            while (schemas.hasNext())
            {
                Schema schema = (Schema)schemas.next();

                if (database.schemaManager.isSystemSchema(schema.name.name))
                {
                    continue;
                }

                if (database.schemaManager.isLobsSchema(schema.name.name))
                {

                    //                continue;
                }

                list.addAll(schema.getTriggerSQL());
                list.addAll(schema.getSequenceRestartSQL());
            }

            if (defaultSchemaHsqlName != null)
            {
                StringBuilder sb = new StringBuilder();

                sb.Append(Tokens.T_SET).Append(' ').Append(Tokens.T_DATABASE);
                sb.Append(' ').Append(Tokens.T_DEFAULT).Append(' ');
                sb.Append(Tokens.T_INITIAL).Append(' ').Append(Tokens.T_SCHEMA);
                sb.Append(' ').Append(defaultSchemaHsqlName.statementName);
                list.add(sb.ToString());
            }

            String[] array = new String[list.size()];

            list.toArray(array);

            return array;
        }

        public String[] getTextTableSQL(bool withHeader)
        {

            EfzArrayList tableList = getAllTables();
            EfzArrayList list = new EfzArrayList();

            for (int i = 0; i < tableList.size(); i++)
            {
                Table t = (Table)tableList.get(i);

                if (!t.isText())
                {
                    continue;
                }

                String[] ddl = t.getSQLForTextSource(withHeader);

                list.addAll(ddl);
            }

            String[] array = new String[list.size()];

            list.toArray(array);

            return array;
        }

        public String[] getIndexRootsSQL()
        {

            Session sysSession = database.sessionManager.getSysSession();
            int[][] rootsArray = getIndexRoots(sysSession);
            EfzArrayList tableList = getAllTables();
            EfzArrayList list = new EfzArrayList();

            for (int i = 0; i < rootsArray.Length; i++)
            {
                Table t = (Table)tableList.get(i);

                if (rootsArray[i] != null && rootsArray[i].Length > 0
                        && rootsArray[i][0] != -1)
                {
                    String ddl =
                        ((Table)tableList.get(i)).getIndexRootsSQL(rootsArray[i]);

                    list.add(ddl);
                }
            }

            String[] array = new String[list.size()];

            list.toArray(array);

            return array;
        }

        int[][] tempIndexRoots;

        public void setTempIndexRoots(int[][] roots)
        {
            tempIndexRoots = roots;
        }

        public int[][] getIndexRoots(Session session)
        {

            if (tempIndexRoots != null)
            {
                int[][] roots = tempIndexRoots;

                tempIndexRoots = null;

                return roots;
            }

            EfzArrayList allTables = getAllTables();
            EfzArrayList list = new EfzArrayList();

            for (int i = 0, size = allTables.size(); i < size; i++)
            {
                Table t = (Table)allTables.get(i);

                if (t.getTableType() == TableBase.CACHED_TABLE)
                {
                    int[] roots = t.getIndexRootsArray();

                    list.add(roots);
                }
                else
                {
                    list.add(null);
                }
            }

            int[][] array = new int[list.size()][];

            list.toArray(array);

            return array;
        }

        /**
         * called after the completion of defrag
         */
        public void setIndexRoots(int[][] roots)
        {

            EfzArrayList allTables = database.schemaManager.getAllTables();

            for (int i = 0, size = allTables.size(); i < size; i++)
            {
                Table t = (Table)allTables.get(i);

                if (t.getTableType() == TableBase.CACHED_TABLE)
                {
                    int[] rootsArray = roots[i];

                    if (roots != null)
                    {
                        t.setIndexRoots(rootsArray);
                    }
                }
            }
        }

        public void setDefaultTableType(int type)
        {
            defaultTableType = type;
        }

        public int getDefaultTableType()
        {
            return defaultTableType;
        }

    }
}
